/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Contibutors: Alex Blewitt <Alex.Blewitt@ioshq.com>
//              Markus Oestreicher <oes@zurich.ibm.com>
//              Frank Hoering <fhr@zurich.ibm.com>
//              Nelson Minar <nelson@media.mit.edu>
//              Jim Cakalic <jim_cakalic@na.biomerieux.com>
//              Avy Sharell <asharell@club-internet.fr>
//              Ciaran Treanor <ciaran@xelector.com>
//              Jeff Turner <jeff@socialchange.net.au>
//              Michael Horwitz <MHorwitz@siemens.co.za>
//              Calvin Chan <calvin.chan@hic.gov.au>
//              Aaron Greenhouse <aarong@cs.cmu.edu>
//              Beat Meier <bmeier@infovia.com.ar>
//              Colin Sampaleanu <colinml1@exis.com>
package org.apache.log4j;

import org.apache.log4j.spi.AppenderAttachable;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.HierarchyEventListener;
import org.apache.log4j.helpers.NullEnumeration;
import org.apache.log4j.helpers.AppenderAttachableImpl;

import java.util.Enumeration;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Vector;

/**
 * <font color="#AA2222"><b>This class has been deprecated and replaced by the
 * {@link Logger} <em>subclass</em></b></font>. It will be kept around to
 * preserve backward compatibility until mid 2003.
 * 
 * <p>
 * <code>Logger</code> is a subclass of Category, i.e. it extends Category. In
 * other words, a logger <em>is</em> a category. Thus, all operations that can
 * be performed on a category can be performed on a logger. Internally, whenever
 * log4j is asked to produce a Category object, it will instead produce a Logger
 * object. Log4j 1.2 will <em>never</em> produce Category objects but only
 * <code>Logger</code> instances. In order to preserve backward compatibility,
 * methods that previously accepted category objects still continue to accept
 * category objects.
 * 
 * <p>
 * For example, the following are all legal and will work as expected.
 * 
 * <pre>
 *    // Deprecated form:
 *        Category cat = Category.getInstance(&quot;foo.bar&quot;)
 *    
 *        // Preferred form for retrieving loggers:
 *        Logger logger = Logger.getLogger(&quot;foo.bar&quot;)
 * </pre>
 * 
 * <p>
 * The first form is deprecated and should be avoided.
 * 
 * <p>
 * <b>There is absolutely no need for new client code to use or refer to the
 * <code>Category</code> class.</b> Whenever possible, please avoid referring to
 * it or using it.
 * 
 * <p>
 * See the <a href="../../../../manual.html">short manual</a> for an
 * introduction on this class.
 * <p>
 * See the document entitled <a
 * href="http://www.qos.ch/logging/preparingFor13.html">preparing for log4j
 * 1.3</a> for a more detailed discussion.
 * 
 * @author Ceki G&uuml;lc&uuml;
 * @author Anders Kristensen
 */
public class Category implements AppenderAttachable {

	/**
	 * The hierarchy where categories are attached to by default.
	 */
	// static
	// public
	// final Hierarchy defaultHierarchy = new Hierarchy(new
	// RootCategory(Level.DEBUG));
	/**
	 * The name of this category.
	 */
	protected String name;

	/**
	 * The assigned level of this category. The <code>level</code> variable need
	 * not be assigned a value in which case it is inherited form the hierarchy.
	 */
	volatile protected Level level;

	/**
	 * The parent of this category. All categories have at least one ancestor
	 * which is the root category.
	 */
	volatile protected Category parent;

	/**
	 * The fully qualified name of the Category class. See also the getFQCN
	 * method.
	 */
	private static final String FQCN = Category.class.getName();

	protected ResourceBundle resourceBundle;

	// Categories need to know what Hierarchy they are in
	protected LoggerRepository repository;

	AppenderAttachableImpl aai;

	/**
	 * Additivity is set to true by default, that is children inherit the
	 * appenders of their ancestors by default. If this variable is set to
	 * <code>false</code> then the appenders found in the ancestors of this
	 * category are not used. However, the children of this category will
	 * inherit its appenders, unless the children have their additivity flag set
	 * to <code>false</code> too. See the user manual for more details.
	 */
	protected boolean additive = true;

	/**
	 * This constructor created a new <code>Category</code> instance and sets
	 * its name.
	 * 
	 * <p>
	 * It is intended to be used by sub-classes only. You should not create
	 * categories directly.
	 * 
	 * @param name
	 *            The name of the category.
	 */
	protected Category(String name) {
		this.name = name;
	}

	/**
	 * Add <code>newAppender</code> to the list of appenders of this Category
	 * instance.
	 * 
	 * <p>
	 * If <code>newAppender</code> is already in the list of appenders, then it
	 * won't be added again.
	 */
	synchronized public void addAppender(Appender newAppender) {
		if (aai == null) {
			aai = new AppenderAttachableImpl();
		}
		aai.addAppender(newAppender);
		repository.fireAddAppenderEvent(this, newAppender);
	}

	/**
	 * If <code>assertion</code> parameter is <code>false</code>, then logs
	 * <code>msg</code> as an {@link #error(Object) error} statement.
	 * 
	 * <p>
	 * The <code>assert</code> method has been renamed to <code>assertLog</code>
	 * because <code>assert</code> is a language reserved word in JDK 1.4.
	 * 
	 * @param assertion
	 * @param msg
	 *            The message to print if <code>assertion</code> is false.
	 * @since 1.2
	 */
	public void assertLog(boolean assertion, String msg) {
		if (!assertion)
			this.error(msg);
	}

	/**
	 * Call the appenders in the hierrachy starting at <code>this</code>. If no
	 * appenders could be found, emit a warning.
	 * 
	 * <p>
	 * This method calls all the appenders inherited from the hierarchy
	 * circumventing any evaluation of whether to log or not to log the
	 * particular log request.
	 * 
	 * @param event
	 *            the event to log.
	 */
	public void callAppenders(LoggingEvent event) {
		int writes = 0;

		for (Category c = this; c != null; c = c.parent) {
			// Protected against simultaneous call to addAppender,
			// removeAppender,...
			synchronized (c) {
				if (c.aai != null) {
					writes += c.aai.appendLoopOnAppenders(event);
				}
				if (!c.additive) {
					break;
				}
			}
		}

		if (writes == 0) {
			repository.emitNoAppenderWarning(this);
		}
	}

	/**
	 * Close all attached appenders implementing the AppenderAttachable
	 * interface.
	 * 
	 * @since 1.0
	 */
	synchronized void closeNestedAppenders() {
		Enumeration enumeration = this.getAllAppenders();
		if (enumeration != null) {
			while (enumeration.hasMoreElements()) {
				Appender a = (Appender) enumeration.nextElement();
				if (a instanceof AppenderAttachable) {
					a.close();
				}
			}
		}
	}

	/**
	 * Log a message object with the {@link Level#DEBUG DEBUG} level.
	 * 
	 * <p>
	 * This method first checks if this category is <code>DEBUG</code> enabled
	 * by comparing the level of this category with the {@link Level#DEBUG
	 * DEBUG} level. If this category is <code>DEBUG</code> enabled, then it
	 * converts the message object (passed as parameter) to a string by invoking
	 * the appropriate {@link org.apache.log4j.or.ObjectRenderer}. It then
	 * proceeds to call all the registered appenders in this category and also
	 * higher in the hierarchy depending on the value of the additivity flag.
	 * 
	 * <p>
	 * <b>WARNING</b> Note that passing a {@link Throwable} to this method will
	 * print the name of the <code>Throwable</code> but no stack trace. To print
	 * a stack trace use the {@link #debug(Object, Throwable)} form instead.
	 * 
	 * @param message
	 *            the message object to log.
	 */
	public void debug(Object message) {
		if (repository.isDisabled(Level.DEBUG_INT))
			return;
		if (Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) {
			System.err.println("debug->" + FQCN);
			forcedLog(FQCN, Level.DEBUG, message, null);
		}
	}

	/**
	 * Log a message object with the <code>DEBUG</code> level including the
	 * stack trace of the {@link Throwable} <code>t</code> passed as parameter.
	 * 
	 * <p>
	 * See {@link #debug(Object)} form for more detailed information.
	 * 
	 * @param message
	 *            the message object to log.
	 * @param t
	 *            the exception to log, including its stack trace.
	 */
	public void debug(Object message, Throwable t) {
		if (repository.isDisabled(Level.DEBUG_INT))
			return;
		if (Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, Level.DEBUG, message, t);
	}

	/**
	 * Log a message object with the {@link Level#ERROR ERROR} Level.
	 * 
	 * <p>
	 * This method first checks if this category is <code>ERROR</code> enabled
	 * by comparing the level of this category with {@link Level#ERROR ERROR}
	 * Level. If this category is <code>ERROR</code> enabled, then it converts
	 * the message object passed as parameter to a string by invoking the
	 * appropriate {@link org.apache.log4j.or.ObjectRenderer}. It proceeds to
	 * call all the registered appenders in this category and also higher in the
	 * hierarchy depending on the value of the additivity flag.
	 * 
	 * <p>
	 * <b>WARNING</b> Note that passing a {@link Throwable} to this method will
	 * print the name of the <code>Throwable</code> but no stack trace. To print
	 * a stack trace use the {@link #error(Object, Throwable)} form instead.
	 * 
	 * @param message
	 *            the message object to log
	 */
	public void error(Object message) {
		if (repository.isDisabled(Level.ERROR_INT))
			return;
		if (Level.ERROR.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, Level.ERROR, message, null);
	}

	/**
	 * Log a message object with the <code>ERROR</code> level including the
	 * stack trace of the {@link Throwable} <code>t</code> passed as parameter.
	 * 
	 * <p>
	 * See {@link #error(Object)} form for more detailed information.
	 * 
	 * @param message
	 *            the message object to log.
	 * @param t
	 *            the exception to log, including its stack trace.
	 */
	public void error(Object message, Throwable t) {
		if (repository.isDisabled(Level.ERROR_INT))
			return;
		if (Level.ERROR.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, Level.ERROR, message, t);

	}

	/**
	 * If the named category exists (in the default hierarchy) then it returns a
	 * reference to the category, otherwise it returns <code>null</code>.
	 * 
	 * @deprecated Please use {@link LogManager#exists} instead.
	 * @since 0.8.5
	 */
	public static Logger exists(String name) {
		return LogManager.exists(name);
	}

	/**
	 * Log a message object with the {@link Level#FATAL FATAL} Level.
	 * 
	 * <p>
	 * This method first checks if this category is <code>FATAL</code> enabled
	 * by comparing the level of this category with {@link Level#FATAL FATAL}
	 * Level. If the category is <code>FATAL</code> enabled, then it converts
	 * the message object passed as parameter to a string by invoking the
	 * appropriate {@link org.apache.log4j.or.ObjectRenderer}. It proceeds to
	 * call all the registered appenders in this category and also higher in the
	 * hierarchy depending on the value of the additivity flag.
	 * 
	 * <p>
	 * <b>WARNING</b> Note that passing a {@link Throwable} to this method will
	 * print the name of the Throwable but no stack trace. To print a stack
	 * trace use the {@link #fatal(Object, Throwable)} form instead.
	 * 
	 * @param message
	 *            the message object to log
	 */
	public void fatal(Object message) {
		if (repository.isDisabled(Level.FATAL_INT))
			return;
		if (Level.FATAL.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, Level.FATAL, message, null);
	}

	/**
	 * Log a message object with the <code>FATAL</code> level including the
	 * stack trace of the {@link Throwable} <code>t</code> passed as parameter.
	 * 
	 * <p>
	 * See {@link #fatal(Object)} for more detailed information.
	 * 
	 * @param message
	 *            the message object to log.
	 * @param t
	 *            the exception to log, including its stack trace.
	 */
	public void fatal(Object message, Throwable t) {
		if (repository.isDisabled(Level.FATAL_INT))
			return;
		if (Level.FATAL.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, Level.FATAL, message, t);
	}

	/**
	 * This method creates a new logging event and logs the event without
	 * further checks.
	 */
	protected void forcedLog(String fqcn, Priority level, Object message,
			Throwable t) {
		callAppenders(new LoggingEvent(fqcn, this, level, message, t));
	}

	/**
	 * Get the additivity flag for this Category instance.
	 */
	public boolean getAdditivity() {
		return additive;
	}

	/**
	 * Get the appenders contained in this category as an {@link Enumeration}.
	 * If no appenders can be found, then a {@link NullEnumeration} is returned.
	 * 
	 * @return Enumeration An enumeration of the appenders in this category.
	 */
	synchronized public Enumeration getAllAppenders() {
		if (aai == null)
			return NullEnumeration.getInstance();
		else
			return aai.getAllAppenders();
	}

	/**
	 * Look for the appender named as <code>name</code>.
	 * 
	 * <p>
	 * Return the appender with that name if in the list. Return
	 * <code>null</code> otherwise.
	 */
	synchronized public Appender getAppender(String name) {
		if (aai == null || name == null)
			return null;

		return aai.getAppender(name);
	}

	/**
	 * Starting from this category, search the category hierarchy for a non-null
	 * level and return it. Otherwise, return the level of the root category.
	 * 
	 * <p>
	 * The Category class is designed so that this method executes as quickly as
	 * possible.
	 */
	public Level getEffectiveLevel() {
		for (Category c = this; c != null; c = c.parent) {
			if (c.level != null)
				return c.level;
		}
		return null; // If reached will cause an NullPointerException.
	}

	/**
	 * 
	 * @deprecated Please use the the {@link #getEffectiveLevel} method instead.
	 * */
	public Priority getChainedPriority() {
		for (Category c = this; c != null; c = c.parent) {
			if (c.level != null)
				return c.level;
		}
		return null; // If reached will cause an NullPointerException.
	}

	/**
	 * Returns all the currently defined categories in the default hierarchy as
	 * an {@link java.util.Enumeration Enumeration}.
	 * 
	 * <p>
	 * The root category is <em>not</em> included in the returned
	 * {@link Enumeration}.
	 * 
	 * @deprecated Please use {@link LogManager#getCurrentLoggers()} instead.
	 */
	public static Enumeration getCurrentCategories() {
		return LogManager.getCurrentLoggers();
	}

	/**
	 * Return the default Hierarchy instance.
	 * 
	 * @deprecated Please use {@link LogManager#getLoggerRepository()} instead.
	 * @since 1.0
	 */
	public static LoggerRepository getDefaultHierarchy() {
		return LogManager.getLoggerRepository();
	}

	/**
	 * Return the the {@link Hierarchy} where this <code>Category</code>
	 * instance is attached.
	 * 
	 * @deprecated Please use {@link #getLoggerRepository} instead.
	 * @since 1.1
	 */
	public LoggerRepository getHierarchy() {
		return repository;
	}

	/**
	 * Return the the {@link LoggerRepository} where this <code>Category</code>
	 * is attached.
	 * 
	 * @since 1.2
	 */
	public LoggerRepository getLoggerRepository() {
		return repository;
	}

	/**
	 * @deprecated Make sure to use {@link Logger#getLogger(String)} instead.
	 */
	public static Category getInstance(String name) {
		return LogManager.getLogger(name);
	}

	/**
	 * @deprecated Please make sure to use {@link Logger#getLogger(Class)}
	 *             instead.
	 */
	public static Category getInstance(Class clazz) {
		return LogManager.getLogger(clazz);
	}

	/**
	 * Return the category name.
	 */
	public final String getName() {
		return name;
	}

	/**
	 * Returns the parent of this category. Note that the parent of a given
	 * category may change during the lifetime of the category.
	 * 
	 * <p>
	 * The root category will return <code>null</code>.
	 * 
	 * @since 1.2
	 */
	final public Category getParent() {
		return this.parent;
	}

	/**
	 * Returns the assigned {@link Level}, if any, for this Category.
	 * 
	 * @return Level - the assigned Level, can be <code>null</code>.
	 */
	final public Level getLevel() {
		return this.level;
	}

	/**
	 * @deprecated Please use {@link #getLevel} instead.
	 */
	final public Level getPriority() {
		return this.level;
	}

	/**
	 * @deprecated Please use {@link Logger#getRootLogger()} instead.
	 */
	final public static Category getRoot() {
		return LogManager.getRootLogger();
	}

	/**
	 * Return the <em>inherited</em> {@link ResourceBundle} for this category.
	 * 
	 * <p>
	 * This method walks the hierarchy to find the appropriate resource bundle.
	 * It will return the resource bundle attached to the closest ancestor of
	 * this category, much like the way priorities are searched. In case there
	 * is no bundle in the hierarchy then <code>null</code> is returned.
	 * 
	 * @since 0.9.0
	 */
	public ResourceBundle getResourceBundle() {
		for (Category c = this; c != null; c = c.parent) {
			if (c.resourceBundle != null)
				return c.resourceBundle;
		}
		// It might be the case that there is no resource bundle
		return null;
	}

	/**
	 * Returns the string resource coresponding to <code>key</code> in this
	 * category's inherited resource bundle. See also {@link #getResourceBundle}
	 * .
	 * 
	 * <p>
	 * If the resource cannot be found, then an {@link #error error} message
	 * will be logged complaining about the missing resource.
	 */
	protected String getResourceBundleString(String key) {
		ResourceBundle rb = getResourceBundle();
		// This is one of the rare cases where we can use logging in order
		// to report errors from within log4j.
		if (rb == null) {
			// if(!hierarchy.emittedNoResourceBundleWarning) {
			// error("No resource bundle has been set for category "+name);
			// hierarchy.emittedNoResourceBundleWarning = true;
			// }
			return null;
		} else {
			try {
				return rb.getString(key);
			} catch (MissingResourceException mre) {
				error("No resource is associated with key \"" + key + "\".");
				return null;
			}
		}
	}

	/**
	 * Log a message object with the {@link Level#INFO INFO} Level.
	 * 
	 * <p>
	 * This method first checks if this category is <code>INFO</code> enabled by
	 * comparing the level of this category with {@link Level#INFO INFO} Level.
	 * If the category is <code>INFO</code> enabled, then it converts the
	 * message object passed as parameter to a string by invoking the
	 * appropriate {@link org.apache.log4j.or.ObjectRenderer}. It proceeds to
	 * call all the registered appenders in this category and also higher in the
	 * hierarchy depending on the value of the additivity flag.
	 * 
	 * <p>
	 * <b>WARNING</b> Note that passing a {@link Throwable} to this method will
	 * print the name of the Throwable but no stack trace. To print a stack
	 * trace use the {@link #info(Object, Throwable)} form instead.
	 * 
	 * @param message
	 *            the message object to log
	 */
	public void info(Object message) {
		if (repository.isDisabled(Level.INFO_INT))
			return;
		if (Level.INFO.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, Level.INFO, message, null);
	}

	/**
	 * Log a message object with the <code>INFO</code> level including the stack
	 * trace of the {@link Throwable} <code>t</code> passed as parameter.
	 * 
	 * <p>
	 * See {@link #info(Object)} for more detailed information.
	 * 
	 * @param message
	 *            the message object to log.
	 * @param t
	 *            the exception to log, including its stack trace.
	 */
	public void info(Object message, Throwable t) {
		if (repository.isDisabled(Level.INFO_INT))
			return;
		if (Level.INFO.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, Level.INFO, message, t);
	}

	/**
	 * Is the appender passed as parameter attached to this category?
	 */
	public boolean isAttached(Appender appender) {
		if (appender == null || aai == null)
			return false;
		else {
			return aai.isAttached(appender);
		}
	}

	/**
	 * Check whether this category is enabled for the <code>DEBUG</code> Level.
	 * 
	 * <p>
	 * This function is intended to lessen the computational cost of disabled
	 * log debug statements.
	 * 
	 * <p>
	 * For some <code>cat</code> Category object, when you write,
	 * 
	 * <pre>
	 * cat.debug(&quot;This is entry number: &quot; + i);
	 * </pre>
	 * 
	 * <p>
	 * You incur the cost constructing the message, concatenatiion in this case,
	 * regardless of whether the message is logged or not.
	 * 
	 * <p>
	 * If you are worried about speed, then you should write
	 * 
	 * <pre>
	 * if (cat.isDebugEnabled()) {
	 * 	cat.debug(&quot;This is entry number: &quot; + i);
	 * }
	 * </pre>
	 * 
	 * <p>
	 * This way you will not incur the cost of parameter construction if
	 * debugging is disabled for <code>cat</code>. On the other hand, if the
	 * <code>cat</code> is debug enabled, you will incur the cost of evaluating
	 * whether the category is debug enabled twice. Once in
	 * <code>isDebugEnabled</code> and once in the <code>debug</code>. This is
	 * an insignificant overhead since evaluating a category takes about 1%% of
	 * the time it takes to actually log.
	 * 
	 * @return boolean - <code>true</code> if this category is debug enabled,
	 *         <code>false</code> otherwise.
	 * */
	public boolean isDebugEnabled() {
		if (repository.isDisabled(Level.DEBUG_INT))
			return false;
		return Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel());
	}

	/**
	 * Check whether this category is enabled for a given {@link Level} passed
	 * as parameter.
	 * 
	 * See also {@link #isDebugEnabled}.
	 * 
	 * @return boolean True if this category is enabled for <code>level</code>.
	 */
	public boolean isEnabledFor(Priority level) {
		if (repository.isDisabled(level.level))
			return false;
		return level.isGreaterOrEqual(this.getEffectiveLevel());
	}

	/**
	 * Check whether this category is enabled for the info Level. See also
	 * {@link #isDebugEnabled}.
	 * 
	 * @return boolean - <code>true</code> if this category is enabled for level
	 *         info, <code>false</code> otherwise.
	 */
	public boolean isInfoEnabled() {
		if (repository.isDisabled(Level.INFO_INT))
			return false;
		return Level.INFO.isGreaterOrEqual(this.getEffectiveLevel());
	}

	/**
	 * Log a localized message. The user supplied parameter <code>key</code> is
	 * replaced by its localized version from the resource bundle.
	 * 
	 * @see #setResourceBundle
	 * @since 0.8.4
	 */
	public void l7dlog(Priority priority, String key, Throwable t) {
		if (repository.isDisabled(priority.level)) {
			return;
		}
		if (priority.isGreaterOrEqual(this.getEffectiveLevel())) {
			String msg = getResourceBundleString(key);
			// if message corresponding to 'key' could not be found in the
			// resource bundle, then default to 'key'.
			if (msg == null) {
				msg = key;
			}
			forcedLog(FQCN, priority, msg, t);
		}
	}
	/**
	 * Log a localized and parameterized message. First, the user supplied
	 * <code>key</code> is searched in the resource bundle. Next, the resulting
	 * pattern is formatted using
	 * {@link java.text.MessageFormat#format(String,Object[])} method with the
	 * user supplied object array <code>params</code>.
	 * 
	 * @since 0.8.4
	 */
	public void l7dlog(Priority priority, String key, Object[] params,
			Throwable t) {
		if (repository.isDisabled(priority.level)) {
			return;
		}
		if (priority.isGreaterOrEqual(this.getEffectiveLevel())) {
			String pattern = getResourceBundleString(key);
			String msg;
			if (pattern == null)
				msg = key;
			else
				msg = java.text.MessageFormat.format(pattern, params);
			forcedLog(FQCN, priority, msg, t);
		}
	}

	/**
	 * This generic form is intended to be used by wrappers.
	 */
	public void log(Priority priority, Object message, Throwable t) {
		if (repository.isDisabled(priority.level)) {
			return;
		}
		if (priority.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, priority, message, t);
	}

	/**
	 * This generic form is intended to be used by wrappers.
	 */
	public void log(Priority priority, Object message) {
		if (repository.isDisabled(priority.level)) {
			return;
		}
		if (priority.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, priority, message, null);
	}

	/**
	 * This is the most generic printing method. It is intended to be invoked by
	 * <b>wrapper</b> classes.
	 * 
	 * @param callerFQCN
	 *            The wrapper class' fully qualified class name.
	 * @param level
	 *            The level of the logging request.
	 * @param message
	 *            The message of the logging request.
	 * @param t
	 *            The throwable of the logging request, may be null.
	 */
	public void log(String callerFQCN, Priority level, Object message,
			Throwable t) {
		if (repository.isDisabled(level.level)) {
			return;
		}
		if (level.isGreaterOrEqual(this.getEffectiveLevel())) {
			forcedLog(callerFQCN, level, message, t);
		}
	}

	/**
	 * LoggerRepository forgot the fireRemoveAppenderEvent method, if using the
	 * stock Hierarchy implementation, then call its fireRemove. Custom
	 * repositories can implement HierarchyEventListener if they want remove
	 * notifications.
	 * 
	 * @param appender
	 *            appender, may be null.
	 */
	private void fireRemoveAppenderEvent(final Appender appender) {
		if (appender != null) {
			if (repository instanceof Hierarchy) {
				((Hierarchy) repository)
						.fireRemoveAppenderEvent(this, appender);
			} else if (repository instanceof HierarchyEventListener) {
				((HierarchyEventListener) repository).removeAppenderEvent(this,
						appender);
			}
		}
	}

	/**
	 * Remove all previously added appenders from this Category instance.
	 * 
	 * <p>
	 * This is useful when re-reading configuration information.
	 */
	synchronized public void removeAllAppenders() {
		if (aai != null) {
			Vector appenders = new Vector();
			for (Enumeration iter = aai.getAllAppenders(); iter
					.hasMoreElements();) {
				appenders.add(iter.nextElement());
			}
			aai.removeAllAppenders();
			for (Enumeration iter = appenders.elements(); iter
					.hasMoreElements();) {
				fireRemoveAppenderEvent((Appender) iter.nextElement());
			}
			aai = null;
		}
	}

	/**
	 * Remove the appender passed as parameter form the list of appenders.
	 * 
	 * @since 0.8.2
	 */
	synchronized public void removeAppender(Appender appender) {
		if (appender == null || aai == null)
			return;
		boolean wasAttached = aai.isAttached(appender);
		aai.removeAppender(appender);
		if (wasAttached) {
			fireRemoveAppenderEvent(appender);
		}
	}

	/**
	 * Remove the appender with the name passed as parameter form the list of
	 * appenders.
	 * 
	 * @since 0.8.2
	 */
	synchronized public void removeAppender(String name) {
		if (name == null || aai == null)
			return;
		Appender appender = aai.getAppender(name);
		aai.removeAppender(name);
		if (appender != null) {
			fireRemoveAppenderEvent(appender);
		}
	}

	/**
	 * Set the additivity flag for this Category instance.
	 * 
	 * @since 0.8.1
	 */
	public void setAdditivity(boolean additive) {
		this.additive = additive;
	}

	/**
	 * Only the Hiearchy class can set the hiearchy of a category. Default
	 * package access is MANDATORY here.
	 */
	final void setHierarchy(LoggerRepository repository) {
		this.repository = repository;
	}

	/**
	 * Set the level of this Category. If you are passing any of
	 * <code>Level.DEBUG</code>, <code>Level.INFO</code>,
	 * <code>Level.WARN</code>, <code>Level.ERROR</code>,
	 * <code>Level.FATAL</code> as a parameter, you need to case them as Level.
	 * 
	 * <p>
	 * As in
	 * 
	 * <pre>
	 * logger.setLevel((Level) Level.DEBUG);
	 * </pre>
	 * 
	 * 
	 * <p>
	 * Null values are admitted.
	 */
	public void setLevel(Level level) {
		this.level = level;
	}

	/**
	 * Set the level of this Category.
	 * 
	 * <p>
	 * Null values are admitted.
	 * 
	 * @deprecated Please use {@link #setLevel} instead.
	 */
	public void setPriority(Priority priority) {
		this.level = (Level) priority;
	}

	/**
	 * Set the resource bundle to be used with localized logging methods
	 * {@link #l7dlog(Priority,String,Throwable)} and
	 * {@link #l7dlog(Priority,String,Object[],Throwable)}.
	 * 
	 * @since 0.8.4
	 */
	public void setResourceBundle(ResourceBundle bundle) {
		resourceBundle = bundle;
	}

	/**
	 * Calling this method will <em>safely</em> close and remove all appenders
	 * in all the categories including root contained in the default hierachy.
	 * 
	 * <p>
	 * Some appenders such as {@link org.apache.log4j.net.SocketAppender} and
	 * {@link AsyncAppender} need to be closed before the application exists.
	 * Otherwise, pending logging events might be lost.
	 * 
	 * <p>
	 * The <code>shutdown</code> method is careful to close nested appenders
	 * before closing regular appenders. This is allows configurations where a
	 * regular appender is attached to a category and again to a nested
	 * appender.
	 * 
	 * @deprecated Please use {@link LogManager#shutdown()} instead.
	 * @since 1.0
	 */
	public static void shutdown() {
		LogManager.shutdown();
	}

	/**
	 * Log a message object with the {@link Level#WARN WARN} Level.
	 * 
	 * <p>
	 * This method first checks if this category is <code>WARN</code> enabled by
	 * comparing the level of this category with {@link Level#WARN WARN} Level.
	 * If the category is <code>WARN</code> enabled, then it converts the
	 * message object passed as parameter to a string by invoking the
	 * appropriate {@link org.apache.log4j.or.ObjectRenderer}. It proceeds to
	 * call all the registered appenders in this category and also higher in the
	 * hieararchy depending on the value of the additivity flag.
	 * 
	 * <p>
	 * <b>WARNING</b> Note that passing a {@link Throwable} to this method will
	 * print the name of the Throwable but no stack trace. To print a stack
	 * trace use the {@link #warn(Object, Throwable)} form instead.
	 * <p>
	 * 
	 * @param message
	 *            the message object to log.
	 */
	public void warn(Object message) {
		if (repository.isDisabled(Level.WARN_INT))
			return;

		if (Level.WARN.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, Level.WARN, message, null);
	}

	/**
	 * Log a message with the <code>WARN</code> level including the stack trace
	 * of the {@link Throwable} <code>t</code> passed as parameter.
	 * 
	 * <p>
	 * See {@link #warn(Object)} for more detailed information.
	 * 
	 * @param message
	 *            the message object to log.
	 * @param t
	 *            the exception to log, including its stack trace.
	 */
	public void warn(Object message, Throwable t) {
		if (repository.isDisabled(Level.WARN_INT))
			return;
		if (Level.WARN.isGreaterOrEqual(this.getEffectiveLevel()))
			forcedLog(FQCN, Level.WARN, message, t);
	}
}
